package cn.yuanqiao.archive.utils;


import cn.yuanqiao.common.exception.ServiceException;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.apache.commons.io.FileUtils;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipOutputStream;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.stream.Collectors;

@Component
public class OfdUtil {

    public static String zipOutPath;

    @Value("${ZIP_OUT_PATH}")
    public void setZipOutPath(String zipOutPath){
        OfdUtil.zipOutPath = zipOutPath;
    }


    public static Integer getOfdPage(String ofdPath) {
        try{
            File ofdFile = new File(ofdPath);
            //zipOutPath = "D:/mnt/zip/unzip/";
            if (ofdFile.exists() && ofdFile.length() > 0) {
                zipOutPath = zipOutPath + System.currentTimeMillis();
                if (!new File(zipOutPath).exists()) {
                    new File(zipOutPath).mkdirs();
                }
                decompress(ofdPath, zipOutPath, "UTF-8");
                Integer result = new File(zipOutPath + "/Doc_0/Pages").list().length;
                FileUtils.deleteDirectory(new File(zipOutPath));
                //TODO 暂时定死Doc_0
                return result;
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    /**
     * 输出新的OFD
     * @param pages
     * @param tagetPath
     * @param outPath
     * @throws Exception
     */
    public static void getNewOfd(List<Integer> pages, String tagetPath, String outPath) throws Exception {
        try {
            File ofdFile = new File(tagetPath);
            if (ofdFile.exists() && ofdFile.length() > 0) {
                String zipPath = new File(outPath).getParent() + "/" + System.currentTimeMillis();
                if (!new File(zipPath).exists()) {
                    new File(zipPath).mkdirs();
                }
                decompress(tagetPath, zipPath, "UTF-8");

                DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
                DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
                Document doc = dBuilder.parse(new File(zipPath + "/Doc_0/Document.xml")); // 加载现有的XML文件
                doc.getDocumentElement().normalize();

                NodeList nodeList = doc.getElementsByTagName("ofd:Pages"); // 获取需要删除的元素列表
                for (int i = 0; i < nodeList.getLength(); i++) {
                    Element element = (Element) nodeList.item(i);
                    NodeList pageNodeList = element.getElementsByTagName("ofd:Page");
                    int k = 0;
                    int pageSize = pageNodeList.getLength();
                    for (int j = 0; j < pageSize; j++, k++) {
                        Element e = (Element) pageNodeList.item(k);
                        if (pages.contains((j + 1))) {
                        } else {
                            e.getParentNode().removeChild(e);
                            k--;
                        }
                    }
                }

                TransformerFactory transformerFactory = TransformerFactory.newInstance();
                Transformer transformer = transformerFactory.newTransformer();
                transformer.setOutputProperty(OutputKeys.INDENT, "yes");
                DOMSource source = new DOMSource(doc);

                FileOutputStream outputStream = new FileOutputStream(new File(zipPath + "/Doc_0/Document.xml"));

                // 创建 StreamResult 对象
                StreamResult streamResult = new StreamResult(outputStream);

                transformer.transform(source, streamResult); // 将更改后的文档保存到文件
                outputStream.close();
                List<String> paths = Arrays.stream(new File(zipPath).listFiles()).map(i -> i.getPath()).collect(Collectors.toList());

                //OutputStream output = new ByteArrayOutputStream();
                //ZipOutputStream zos1 = new ZipOutputStream(output);


                ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(outPath));
                zos.setEncoding("UTF-8");
                ofdZip(zipPath, outPath);

                FileUtils.deleteDirectory(new File(zipPath));
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
    }




    /**
     * 输出新的OFD
     * @param pages
     * @param tagetPath
     * @throws Exception
     */
    public static InputStream extractPages(List<Integer> pages, String tagetPath) throws Exception {
        try {
            //zipOutPath = "D:/mnt/zip/unzip/";
             File ofdFile = new File(tagetPath);
            String zipPath = "";
            if (ofdFile.exists() && ofdFile.length() > 0) {
                zipPath = new File(zipOutPath).getParent() + "/" + System.currentTimeMillis();
                if (!new File(zipPath).exists()) {
                    new File(zipPath).mkdirs();
                }
                decompress(tagetPath, zipPath, "UTF-8");

                DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
                DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
                Document doc = dBuilder.parse(new File(zipPath + "/Doc_0/Document.xml")); // 加载现有的XML文件
                doc.getDocumentElement().normalize();

                NodeList nodeList = doc.getElementsByTagName("ofd:Pages"); // 获取需要删除的元素列表
                for (int i = 0; i < nodeList.getLength(); i++) {
                    Element element = (Element) nodeList.item(i);
                    NodeList pageNodeList = element.getElementsByTagName("ofd:Page");
                    int k = 0;
                    int pageSize = pageNodeList.getLength();
                    for (int j = 0; j < pageSize; j++, k++) {
                        Element e = (Element) pageNodeList.item(k);
                        if (pages.contains((j + 1))) {
                        } else {
                            e.getParentNode().removeChild(e);
                            k--;
                        }
                    }
                }

                TransformerFactory transformerFactory = TransformerFactory.newInstance();
                Transformer transformer = transformerFactory.newTransformer();
                transformer.setOutputProperty(OutputKeys.INDENT, "yes");
                DOMSource source = new DOMSource(doc);
                FileOutputStream outputStream = new FileOutputStream(new File(zipPath + "/Doc_0/Document.xml"));
                // 创建 StreamResult 对象
                StreamResult streamResult = new StreamResult(outputStream);
                transformer.transform(source, streamResult); // 将更改后的文档保存到文件
                outputStream.close();

                OutputStream output = new ByteArrayOutputStream();
                ZipOutputStream zos = new ZipOutputStream(output);

                File file = new File(zipPath);
                File[] listFiles = file.listFiles();
                for(File f : listFiles) {
                    if(f.isDirectory()){
                        // 如果直接压缩文件夹
                        ZIPDIR(f.getPath(), zos, f.getName() + "/");// 此处使用/来表示目录，如果使用\\来表示目录的话，会导致压缩后的文件目录组织形式在解压缩的时候不能正确识别。
                    } else{
                        // 如果直接压缩文件
                        ZIPFILE(f.getPath(), zos, f.getName());
                    }
                }
                zos.closeEntry();
                zos.close();
                FileUtils.deleteDirectory(new File(zipPath));
                return new ByteArrayInputStream(((ByteArrayOutputStream) output).toByteArray());
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    public static void main(String[] args) throws Exception {
        //List<String> paths = Arrays.stream(new File("D:\\mnt\\zip\\unzip\\1713543750461").listFiles()).map(i->i.getPath()).collect(Collectors.toList());
        //cn.yuanqiao.archive.common.util.ZipUtil.zip(Arrays.stream(new File("D:\\mnt\\zip\\unzip\\1713543750461").listFiles()).map(i->i).collect(Collectors.toList()), new ZipOutputStream(new FileOutputStream("D:\\mnt\\zip\\unzip\\1234.ofd")),"/");
        //ZipUcompress(new File("D:/mnt/zip/unzip/1713543750461/"),"D:\\mnt\\zip\\unzip\\1234.ofd","1234",true);
        //FileInputStream fis = new FileInputStream("D:/mnt/zip/unzip/1713543750461/Doc_0");
        List<Integer> list = new ArrayList<Integer>();
        list.add(1);
        //list.add(2);
        //list.add(50);
        //getNewOfd(list, "D:\\mnt\\zip\\unzip\\1234.ofd", "D:/mnt/zip/unzip/1234.ofd");
        InputStream inputStream = extractPages(list,"D:/opt/mnt/\\004\\LTDA\\304191407491518464\\30419140749151846401A005.ofd");
        OutputStream out = new FileOutputStream("C:\\Users\\Administrator\\Desktop\\2.ofd");
        int len = 0;
        byte[] buf = new byte[4096];

        while ((len = inputStream.read(buf)) > 0) {
            out.write(buf, 0, len);
            out.flush(); // 定期刷新输出流
        }
        System.gc();
        inputStream.close();
        out.close();
    }

    public static void ofdZip(String source, String zipFileName) throws IOException {
        ZipOutputStream zos = new ZipOutputStream(new File(zipFileName));
        zos.setEncoding("gb2312");
        File file = new File(source);
        File[] listFiles = file.listFiles();
        for(File f : listFiles) {
            if(f.isDirectory()){
                // 如果直接压缩文件夹
                ZIPDIR(f.getPath(), zos, f.getName() + "/");// 此处使用/来表示目录，如果使用\\来表示目录的话，会导致压缩后的文件目录组织形式在解压缩的时候不能正确识别。
            } else{
                // 如果直接压缩文件
                //ZIPDIR(f.getPath(), zos, "/");
                ZIPFILE(f.getPath(), zos, f.getName());
            }
        }
        zos.closeEntry();
        zos.close();

    }

    public static void zip(String source, String zipFileName) throws IOException {
        ZipOutputStream zos = new ZipOutputStream(new File(zipFileName));
        zos.setEncoding("gb2312");
        File file = new File(source);
        if(file.isDirectory()){
            // 如果直接压缩文件夹
            ZIPDIR(source, zos, file.getName() + "/");// 此处使用/来表示目录，如果使用\\来表示目录的话，会导致压缩后的文件目录组织形式在解压缩的时候不能正确识别。
        } else{
            // 如果直接压缩文件
            ZIPDIR(source, zos, new File(file.getParent()).getName() + "/");
            ZIPFILE(source, zos, new File(file.getParent()).getName() + "/" + file.getName());
        }
        zos.closeEntry();
        zos.close();
    }

    private static void ZIPDIR(String sourceDir, ZipOutputStream zos, String target) throws IOException {
        ZipEntry ze = new ZipEntry(target);
        zos.putNextEntry(ze);
        // 提取要压缩的文件夹中的所有文件
        File f = new File(sourceDir);
        File[] fileList = f.listFiles();
        if (fileList != null) {
            // 如果该文件夹下有文件则提取所有的文件进行压缩
            for (File subFile : fileList) {
                if (subFile.isDirectory()) {
                    // 如果是目录则进行目录压缩
                    ZIPDIR(subFile.getPath(), zos, target + subFile.getName() + "/");
                } else {
                    // 如果是文件，则进行文件压缩
                    ZIPFILE(subFile.getPath(), zos, target + subFile.getName());
                }
            }
        }
    }

    /**
     * 压缩文件
     *
     * @param sourceFileName 要压缩的文件
     * @param zos 压缩输出流
     * @param target 压缩后文件的目录(父文件夹)
     * @throws IOException
     */
    public static void ZIPFILE(String sourceFileName, ZipOutputStream zos, String target) throws IOException {
        ZipEntry ze = new ZipEntry(target);
        zos.putNextEntry(ze);
        FileInputStream fis = new FileInputStream(sourceFileName);
        byte[] buffer = new byte[1024];
        int location = 0;
        while((location = fis.read(buffer)) != -1) {
            zos.write(buffer, 0, location);
        }
        fis.close();
    }

    public static void decompress(String zipFilePath, String descDir,String charset) {
        File zipFile = new File(zipFilePath);
        File pathFile = new File(descDir);
        if (!pathFile.exists()) {
            pathFile.mkdirs();
        }
        try (ZipFile zip = new ZipFile(zipFile,charset)) {
            Enumeration<ZipArchiveEntry> entries = zip.getEntries();
            while (entries.hasMoreElements()) {
                // 遍历获取压缩文件内全部条目，包括子条目中的条目
                ZipArchiveEntry entry = entries.nextElement();
                String entryName = entry.getName();
                try {
                    InputStream in = zip.getInputStream(entry);
                    String outPath = (descDir + "/" + entryName);
                    // 判断路径是否存在，不存在则创建文件路径
                    File file = new File(outPath.substring(0, outPath.lastIndexOf('/')));
                    if (!file.exists()) {
                        file.mkdirs();
                    }                // 判断文件全路径是否为文件夹,如果是上面已经创建,不需要解压
                    if (new File(outPath).isDirectory()) {
                        continue;
                    }
                    try {
                        OutputStream out = new FileOutputStream(outPath);
                        byte[] buf = new byte[4 * 1024];
                        int len = 0;
                        while ((len = in.read(buf)) >= 0) {
                            out.write(buf, 0, len);
                        }
                        out.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                        throw new ServiceException("解压失败");
                    }
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    throw new ServiceException("解压失败");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException("解压失败");
        }
    }

}
